package bb.lanxing.mvp.presetner;

import static bb.lanxing.model.lushu.WayPoint.STATUS_NONE;
import static bb.lanxing.model.lushu.WayPoint.STATUS_SELECTED;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.core.PoiInfo;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.geocode.GeoCodeOption;
import com.baidu.mapapi.search.geocode.GeoCodeResult;
import com.baidu.mapapi.search.geocode.GeoCoder;
import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;
import com.baidu.mapapi.search.poi.OnGetPoiSearchResultListener;
import com.baidu.mapapi.search.poi.PoiDetailResult;
import com.baidu.mapapi.search.poi.PoiDetailSearchResult;
import com.baidu.mapapi.search.poi.PoiIndoorResult;
import com.baidu.mapapi.search.poi.PoiNearbySearchOption;
import com.baidu.mapapi.search.poi.PoiResult;
import com.baidu.mapapi.search.poi.PoiSearch;
import com.github.mikephil.charting.utils.Utils;

import android.text.TextUtils;

import androidx.fragment.app.FragmentActivity;

import bb.lanxing.App;
import bb.lanxing.R;
import bb.lanxing.lib.kt.model.geo.IGeoPoint;
import bb.lanxing.manager.SharedManager;
import bb.lanxing.model.database.RouteBook;
import bb.lanxing.model.lushu.WayPoint;
import bb.lanxing.model.lushu.WayPointBuilder;
import bb.lanxing.model.lushu.WayPointBuilderImpl;
import bb.lanxing.model.lushu.WayPointImpl;
import bb.lanxing.mvp.model.BaiduRouteBookSourceModelImpl;
import bb.lanxing.mvp.model.i.RouteBookSourceModel;
import bb.lanxing.mvp.presetner.i.CreateRouteBookPresenter;
import bb.lanxing.mvp.view.i.CreateRouteBookView;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

public class CreateRouteBookPresenterImpl extends BasePresenter
        implements CreateRouteBookPresenter, OnGetGeoCoderResultListener, OnGetPoiSearchResultListener {
    private final CreateRouteBookView createRouteBookView;
    private final GeoCoder geoCoder;
    private final PoiSearch poiSearch;
    private final LinkedList<WayPoint> reverseCodeQueue;
    private String searchKeyword;
    private int selectedIndex;
    private RouteBookSourceModel sourceModel;

    public CreateRouteBookPresenterImpl(CreateRouteBookView createRouteBookView) {
        this.selectedIndex = 0;
        this.reverseCodeQueue = new LinkedList<>();
        this.createRouteBookView = createRouteBookView;
        GeoCoder newInstance = GeoCoder.newInstance();
        this.geoCoder = newInstance;
        newInstance.setOnGetGeoCodeResultListener(this);
        PoiSearch newInstance2 = PoiSearch.newInstance();
        this.poiSearch = newInstance2;
        newInstance2.setOnGetPoiSearchResultListener(this);
    }

    public CreateRouteBookPresenterImpl(int sourceType, CreateRouteBookView view) {
        this(view);
        this.sourceModel = new BaiduRouteBookSourceModelImpl(sourceType);
    }

    @Override
    public void onGetPoiDetailResult(PoiDetailResult poiDetailResult) {
    }

    @Override
    public void onGetPoiDetailResult(PoiDetailSearchResult poiDetailSearchResult) {
    }

    @Override
    public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {
    }

    @Override
    public void removeAllWayPoint() {
        sourceModel.removeAllWayPoint();
    }

    @Override
    public void addWayPoints(WayPoint... point) {
        this.sourceModel.addWayPoints(point);
    }

    @Override
    public int insertWayPointBefore(WayPoint before, WayPoint wayPoint) {
        int indexOf = indexOf(before);
        if (indexOf == -1) {
            throw new IllegalArgumentException("Not found WayPoint with argument before");
        }
        this.sourceModel.addWayPointsAt(indexOf, wayPoint);
        return indexOf;
    }

    @Override
    public int insertWayPointAfter(WayPoint after, WayPoint wayPoint) {
        int indexOf = indexOf(after);
        if (indexOf == -1) {
            throw new IllegalArgumentException("Not found WayPoint with argument after");
        }
        this.sourceModel.addWayPointsAt(indexOf + 1, wayPoint);
        return indexOf;
    }

    @Override
    public WayPoint getWayPointAt(int index) {
        return this.sourceModel.getWayPointAt(index);
    }

    @Override
    public void setWayPointAt(int wayPointAt, WayPoint wayPoint) {
        this.sourceModel.setWayPointAt(wayPointAt, wayPoint);
    }

    @Override
    public int indexOf(WayPoint wayPoint) {
        return this.sourceModel.indexOf(wayPoint);
    }

    @Override
    public List<WayPoint> getWayPoints() {
        return this.sourceModel.getWayPoints();
    }

    @Override
    public WayPoint getEndWayPoint() {
        return this.sourceModel.getWayPointAt(getWayPointSize() - 1);
    }

    @Override
    public WayPoint getStartWayPoint() {
        return this.sourceModel.getWayPointAt(0);
    }

    @Override
    public WayPoint getSelectedWayPoint() {
        return this.sourceModel.getWayPointAt(this.selectedIndex);
    }

    @Override
    public void setStatusForWayPoint(WayPoint wayPoint, int status) {
        setStatusForWayPoint(wayPoint, status, true);
    }

    private void setStatusForWayPoint(WayPoint wayPoint, int status, boolean refresh) {
        if (wayPoint == null) {
            return;
        }
        if (wayPoint instanceof WayPointImpl wayPointImpl) {
            wayPointImpl.setStatus(status);
        }
        if (createRouteBookView == null || !refresh) {
            return;
        }
        createRouteBookView.onRefreshView();
    }

    @Override
    public void setTagForWayPoint(WayPoint wayPoint, Object tag) {
        if (wayPoint instanceof WayPointImpl) {
            ((WayPointImpl) wayPoint).setTag(tag);
        }
    }

    @Override
    public void reverseCodeForWayPoint(WayPoint wayPoint) {
        reverseGeoCode(wayPoint);
    }

    @Override
    public int getSelectedIndex() {
        return this.selectedIndex;
    }

    @Override
    public int getWayPointSize() {
        return this.sourceModel.getWayPointSize();
    }

    @Override
    public Iterator<WayPoint> iterator() {
        return this.sourceModel.getWayPoints().iterator();
    }

    @Override
    public WayPoint removeAt(int index) {
        WayPoint removeAt = this.sourceModel.removeAt(index);
        notifyDrawRouteBook(false);
        if (this.selectedIndex >= index) {
            selectWayPoint(index - 1);
        }
        return removeAt;
    }

    @Override
    public void selectWayPoint(int index) {
        if (index < 0 || index > getWayPointSize()) {
            return;
        }
        for (int i = 0; i < getWayPointSize(); i++) {
            setStatusForWayPoint(getWayPointAt(i), STATUS_NONE, false);
        }
        this.selectedIndex = index;
        WayPoint wayPointAt = getWayPointAt(index);
        setStatusForWayPoint(wayPointAt, STATUS_SELECTED, false);
        if (createRouteBookView != null) {
            createRouteBookView.onSelectPoint(wayPointAt);
        }
    }

    @Override
    public void search(String keyword) {
        if (this.createRouteBookView == null) {
            return;
        }
        if (TextUtils.isEmpty(keyword)) {
            this.createRouteBookView.toast(R.string.lushu_toast_input_search);
            return;
        }
        this.searchKeyword = keyword;
        this.createRouteBookView.showLoadingDialog(R.string.dialog_searching, true);
        String curCityWithMP = TextUtils.isEmpty("") ? SharedManager.getInstance().getCurCityWithMP() : "";
        if (TextUtils.isEmpty(curCityWithMP)) {
            curCityWithMP = "上海";
        }
        try {
            this.geoCoder.geocode(new GeoCodeOption().address(keyword).city(curCityWithMP));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void searchForPoi(String keyword, LatLng latLng) {
        PoiNearbySearchOption poiNearbySearchOption = new PoiNearbySearchOption();
        poiNearbySearchOption.location(latLng);
        poiNearbySearchOption.keyword(keyword);
        poiNearbySearchOption.radius(1000);
        poiNearbySearchOption.pageCapacity(50);
        poiNearbySearchOption.pageNum(0);
        this.poiSearch.searchNearby(poiNearbySearchOption);
    }

    @Override
    public WayPointBuilder newWayPointBuilder() {
        return new WayPointBuilderImpl();
    }

    @Override
    public void build() {
        if (createRouteBookView == null) {
            return;
        }
        createRouteBookView.showLoadingDialog(R.string.map_loading_track, true);
        addSubscription(Observable.just(Boolean.TRUE).flatMap(aBoolean -> {
            if (sourceModel == null) {
                return Observable.empty();
            }
            try {
                return Observable.just(sourceModel.build());
            } catch (IOException e) {
                return Observable.error(e);
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<>() {
            @Override
            public void onCompleted() {
                createRouteBookView.dismissLoadingDialog();
            }

            @Override
            public void onError(Throwable throwable) {
                if (isUnsubscribed() || !(throwable instanceof IOException)) {
                    return;
                }
                createRouteBookView.toast(R.string.network_err);
                createRouteBookView.dismissLoadingDialog();
            }

            @Override
            public void onNext(RouteBook routeBook) {
                if (isUnsubscribed()) {
                    return;
                }
                if (routeBook == null) {
                    createRouteBookView.toast(R.string.toast_save_failure);
                } else {
                    createRouteBookView.onRouteBook(routeBook);
                }
            }
        }));
    }

    @Override
    public void reBuild(final RouteBook routeBookArg) {
        if (createRouteBookView == null) {
            return;
        }
        createRouteBookView.showLoadingDialog(R.string.map_loading_track, true);
        addSubscription(Observable.just(Boolean.TRUE).flatMap((Func1<Boolean, Observable<RouteBook>>) aBoolean -> {
            if (sourceModel == null) {
                return Observable.empty();
            }
            try {
                return Observable.just(sourceModel.reBuild(routeBookArg));
            } catch (IOException e) {
                return Observable.error(e);
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<>() {
            @Override
            public void onCompleted() {
                createRouteBookView.dismissLoadingDialog();
            }

            @Override
            public void onError(Throwable throwable) {
                if (isUnsubscribed() || !(throwable instanceof IOException)) {
                    return;
                }
                createRouteBookView.toast(R.string.network_err);
                createRouteBookView.dismissLoadingDialog();
            }

            @Override
            public void onNext(RouteBook routeBook) {
                if (isUnsubscribed()) {
                    return;
                }
                createRouteBookView.onRouteBook(routeBook);
            }
        }));
    }

    @Override
    public void preview() {
        for (WayPoint wayPoint : getWayPoints()) {
            setStatusForWayPoint(wayPoint, STATUS_NONE, false);
        }
        notifyDrawRouteBook(true);
    }

    @Override
    public double getDistance() {
        return sourceModel != null ? sourceModel.getDistance() : Utils.DOUBLE_EPSILON;
    }

    @Override
    public void destroy() {
        super.destroy();
        if (sourceModel != null) {
            sourceModel.release();
        }
    }

    private void reverseGeoCode(WayPoint wayPoint) {
        if (indexOf(wayPoint) == -1) {
            throw new IllegalStateException("Not found way point.");
        }
        this.reverseCodeQueue.add(wayPoint);
        ReverseGeoCodeOption reverseGeoCodeOption = new ReverseGeoCodeOption();
        IGeoPoint baidu = wayPoint.toBaidu(true);
        reverseGeoCodeOption.location(new LatLng(baidu.getLatitude(), baidu.getLongitude()));
        this.geoCoder.reverseGeoCode(reverseGeoCodeOption);
    }

    private void notifyDrawRouteBook(final boolean inBound) {
        Observable.just(getWayPoints()).flatMap(wayPoints -> {
            if (sourceModel == null) {
                return Observable.empty();
            }
            try {
                return Observable.just(sourceModel.preview());
            } catch (IOException e) {
                return Observable.error(e);
            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<>() {
            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable throwable) {
                String msg = throwable.getMessage();
                if (!TextUtils.isEmpty(msg)) {
                    if (msg.contains("2002")) {
                        App.getContext()
                                .showLongMessage(App.getContext().getString(R.string.routebook_line_too_long_error));
                    }
                } else {
                    throwable.printStackTrace();
                }
            }

            @Override
            public void onNext(List<IGeoPoint> geoPoints) {
                if (createRouteBookView == null || isUnsubscribed()) {
                    return;
                }
                createRouteBookView.onRefreshView();
                createRouteBookView.onDraw(geoPoints, inBound);
            }
        });
    }

    @Override
    public void onGetGeoCodeResult(GeoCodeResult geoCodeResult) {
        if (this.createRouteBookView == null) {
            return;
        }
        if (geoCodeResult.error == SearchResult.ERRORNO.NO_ERROR) {
            geoCodeResult.getAddress();
            searchForPoi(this.searchKeyword, geoCodeResult.getLocation());
            return;
        }
        this.createRouteBookView.dismissLoadingDialog();
        this.createRouteBookView.toast(R.string.toast_find_no_result);
    }

    @Override
    public void onGetReverseGeoCodeResult(ReverseGeoCodeResult reverseGeoCodeResult) {
        FragmentActivity activity;
        WayPoint removeFirst = this.reverseCodeQueue.removeFirst();
        if (createRouteBookView == null || removeFirst == null
                || (activity = createRouteBookView.getActivity()) == null) {
            return;
        }
        String address = reverseGeoCodeResult.getAddress();
        String str = null;
        int i = removeFirst.getStatus() != 0 ? 3 : 0;
        if (TextUtils.isEmpty(address)) {
            address = "";
        } else if (TextUtils.isEmpty(removeFirst.getContent())) {
            str = address;
        }
        int indexOf = indexOf(removeFirst);
        if (indexOf != -1) {
            setWayPointAt(indexOf, removeFirst.newBuilder().address(address).content(str).status(i).build());
            createRouteBookView.onRefreshView();
            notifyDrawRouteBook(false);
        }
    }

    @Override
    public void onGetPoiResult(PoiResult poiResult) {
        if (createRouteBookView == null) {
            return;
        }
        createRouteBookView.dismissLoadingDialog();
        if (poiResult.error == SearchResult.ERRORNO.NO_ERROR) {
            List<PoiInfo> allPoi = poiResult.getAllPoi();
            ArrayList<WayPoint> arrayList = new ArrayList<>();
            for (PoiInfo poiInfo : allPoi) {
                arrayList.add(
                        newWayPointBuilder().address(poiInfo.address).title(poiInfo.name).lat(poiInfo.location.latitude)
                                .lng(poiInfo.location.longitude).content(poiInfo.city).type(2).build());
            }
            this.createRouteBookView.onSearchResult(arrayList);
            return;
        }
        this.createRouteBookView.toast(R.string.toast_find_no_result);
    }
}
